/*
 * Parser.cpp
 *
 *  Created on: 17/09/2012
 *      Author: emma
 */

#include "Parser.h"



Parser::Parser(string root) {

	//inicializo la referencia al offset del path
	this->offsetAlPath = 0;

	//Guardo la raiz
	guardarPath(root);

	this->raiz = root;

	//inicializo el generador de idDocumento
	this->idDocumento = 0;

	//Creo un lista de objetos para ir guardando informacion necesaria para el merge
	this->listaDeInfoMerge = new list<InfoMerge>();

}

int Parser::guardarPath(string path){

	fstream archivoPaths;
	archivoPaths.open("ArchivoPaths.txt", fstream::app | fstream::out);

	if (!archivoPaths.is_open()) {
		/* limpia los flags de control de estado del archivo */
		archivoPaths.clear();
		throw string("El archivo no pudo ser abierto para escritura");
	}

	pasarStringATamanioFijo(path,38);

	archivoPaths << path;

	int offset = this->offsetAlPath;
	this->offsetAlPath = this->offsetAlPath + 38;

	archivoPaths.close();

	return offset;

}

void Parser::pasarStringATamanioFijo(string &cadena, int tamanioFijo){
	int cantidadARellenar = tamanioFijo - cadena.length();

	for(int i = 0; i < cantidadARellenar; i++){
		cadena = cadena + " ";
	}
}

void Parser::rellenarConCeros(string &cadena){
	int cantidadARellenar = 3 - cadena.length();

	for(int i = 0; i < cantidadARellenar; i++){
		cadena = "0" + cadena;
	}
}

void Parser::parsear(string path){

	//extrae los tokens
	list<string>* tokens = tokenizar(path);

	//ordena lexicograficamente, obtiene frecuencia y elimina duplicados
	list<RegistroTermino>* terminos = generarTerminosYFrecuencias(tokens);

	//Guarda informacion que utilizara el merge de archivos auxiliares
	guardarInfoMerge(terminos);

	//guarda los terminos en un archivo auxiliar
	generarAuxiliar(terminos);

	//guarda el path del documento de texto procesado
	//le borra primer parte que es la raiz, que ya esta guardada
	path.erase(0,this->raiz.length());
	guardarPath(path);

}

void Parser::guardarInfoMerge(list<RegistroTermino>* terminos){
	//guardo la cantidad de registros y el idDoc en un objeto infoMerge
	InfoMerge* info = new InfoMerge(terminos->size(), this->idDocumento);
	this->listaDeInfoMerge->push_back(*info);
}

list<InfoMerge>* Parser::obtenerInfoMerge(){
	return this->listaDeInfoMerge;
}

int Parser::generarAuxiliar(list<RegistroTermino>* terminos){
	//Paso el idDocumento a string
	int idDoc = this->idDocumento;
	string stringIdDoc = convertirIntAString(idDoc);
	rellenarConCeros(stringIdDoc);
	string nombreArchivoAuxiliar = "ArchivoAuxiliar" + stringIdDoc + ".txt";

	fstream archivoAuxiliar;
	archivoAuxiliar.open(nombreArchivoAuxiliar.c_str(), fstream::out);

	//escribo al principio del archivo el id del documento
	pasarStringATamanioFijo(stringIdDoc, 3);
	archivoAuxiliar << stringIdDoc;

	list<RegistroTermino>::iterator it;
	for(it=terminos->begin(); it!=terminos->end(); it++){
		RegistroTermino unRegistroTermino = *it;
		string termino = unRegistroTermino.obtenerTermino();
		//Paso el termino a un tamaño fijo de 20 bytes
		pasarStringATamanioFijo(termino,20);

		int frecuencia = unRegistroTermino.obtenerFrecuencia();
		//Paso la frecuencia a un string
		string stringFrecuencia = convertirIntAString(frecuencia);
		//Paso stringFrecuencia a tamanio fijo de 4 bytes
		pasarStringATamanioFijo(stringFrecuencia, 4);

		//Escribo el termino y la frecuencia
		archivoAuxiliar << termino;
		archivoAuxiliar << stringFrecuencia;
	}

	delete terminos;

	//Aumento el id de documentos
	this->idDocumento++;
	archivoAuxiliar.close();

	return idDoc;
}

string Parser::convertirIntAString(int entero){
	ostringstream flujoDeConversion;
	flujoDeConversion << entero;
	return flujoDeConversion.str();
}

list<RegistroTermino>* Parser::generarTerminosYFrecuencias(list<string>* tokens){
	tokens->sort();

	list<RegistroTermino>* terminos = new list<RegistroTermino>();

	list<string>::iterator it = tokens->begin();
	string unToken = *it;
	it++;
	int frecuencia = 1;

	while(it!=tokens->end()){
		string tokenSiguiente = *it;
		frecuencia = 1;
		while((unToken == tokenSiguiente) && (it!=tokens->end())){
			frecuencia++;
			it++;
			if(it!=tokens->end())
				tokenSiguiente = *it;
		}
		RegistroTermino* unRegistroTermino = new RegistroTermino(unToken, frecuencia);
		terminos->push_back(*unRegistroTermino);
		unToken = tokenSiguiente;
		if(it!=tokens->end())
			it++;
	}

	delete tokens;
	return terminos;

}

void Parser::pasarAMinuscula(string &token){
	string::iterator iter;

	for( iter = token.begin(); iter != token.end(); ++iter ) {
			*iter = tolower(*iter);
	}

}

void Parser::quitarAcentosYDieresis(string &token){
	map<string,string> caracteres;
	map<string,string>::iterator it;
	string caracter;
	string caracter_reemplazo;
	size_t found;

	caracteres.insert (pair<string,string>("á","a") );
	caracteres.insert (pair<string,string>("é","e") );
	caracteres.insert (pair<string,string>("í","i") );
	caracteres.insert (pair<string,string>("ó","o") );
	caracteres.insert (pair<string,string>("ú","u") );
	//Las paso directamente a minuscula
	caracteres.insert (pair<string,string>("Á","a") );
	caracteres.insert (pair<string,string>("É","e") );
	caracteres.insert (pair<string,string>("Í","i") );
	caracteres.insert (pair<string,string>("Ó","o") );
	caracteres.insert (pair<string,string>("Ú","u") );
	caracteres.insert (pair<string,string>("ä","a") );
	caracteres.insert (pair<string,string>("ë","e") );
	caracteres.insert (pair<string,string>("ï","i") );
	caracteres.insert (pair<string,string>("ö","o") );
	caracteres.insert (pair<string,string>("ü","u") );
	caracteres.insert (pair<string,string>("Ä","a") );
	caracteres.insert (pair<string,string>("Ë","e") );
	caracteres.insert (pair<string,string>("Ï","i") );
	caracteres.insert (pair<string,string>("Ö","o") );
	caracteres.insert (pair<string,string>("Ü","u") );
	//caracteres.insert (pair<string,string>("ñ","n") );

	for (it= caracteres.begin(); it != caracteres.end(); it++){
		caracter=(it->first);
		found = token.find(caracter);

		if(found != string::npos){
			caracter_reemplazo=(it->second);

			token.erase(found,caracter.length());
			token.replace(found,0,caracter_reemplazo,0,1);
		}
	}
}

void Parser::quitarSignosDePuntuacion(string &token){
	size_t pos;
	pos = token.find_first_of(".,;:()\"{}[]?¿!¡_-'—");

	while (pos!=string::npos){
		token.erase(pos,1);
		pos=token.find_first_of(".,;:()\"{}[]?¿!¡_-'—",pos);
	}

}

list<string>* Parser::tokenizar(string path){
	fstream documento;

	//abro el documento para lectura
	documento.open(path.c_str(), fstream::in);

	if (!documento.is_open()) {
		/* limpia los flags de control de estado del archivo */
		documento.clear();
		throw string("El documento no pudo ser abierto para lectura");
	}

	list<string>* tokens = new list<string>();

	char linea[256];
	//leo una linea
	documento.getline(linea, 256, '\n');
	while(!documento.eof()){

		//paso la linea a un string
		string cadena (linea);

		//busco tokens en cadena delimitados por blancos o guiones
		size_t pos = cadena.find_first_of(" -");

		while(pos != string::npos){
			string unToken = cadena.substr(0,pos);
			pasarAMinuscula(unToken);
			quitarAcentosYDieresis(unToken);
			quitarSignosDePuntuacion(unToken);
			if(unToken != "")//Chequeo que el token no este vacio
				tokens->push_back(unToken);
			cadena.erase(0,pos+1);
			pos = cadena.find_first_of(" -");
		}

		//agrego el pedazo de cadena que quedo al final que es un token
		if (cadena.length() != 0){
			//le quito el /0 del final
			cadena.erase(cadena.length()-1,1);
			pasarAMinuscula(cadena);
			quitarAcentosYDieresis(cadena);
			quitarSignosDePuntuacion(cadena);
			if(cadena != "")//Chequeo que el token no este vacio
				tokens->push_back(cadena);
		}

		//leo una linea
		documento.getline(linea, 256, '\n');
	}
	documento.close();

	return tokens;
}

Parser::~Parser() {
}

